/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Engine2D.math;

/**
 *
 * @author Garys
 */
public class Matrix4f {

    /**
     * 
     */
    protected float[][] matrix = new float[3][3];

    /**
     * 
     * @param matrix
     */
    public Matrix4f(float[] matrix) {
        this.matrix[0][0] = matrix[0];
        this.matrix[0][1] = matrix[1];
        this.matrix[0][2] = matrix[2];
        this.matrix[0][3] = matrix[3];
        this.matrix[1][0] = matrix[4];
        this.matrix[1][1] = matrix[5];
        this.matrix[1][2] = matrix[6];
        this.matrix[1][3] = matrix[7];
        this.matrix[2][0] = matrix[8];
        this.matrix[2][1] = matrix[9];
        this.matrix[2][2] = matrix[10];
        this.matrix[2][3] = matrix[11];
        this.matrix[3][0] = matrix[12];
        this.matrix[3][1] = matrix[13];
        this.matrix[3][2] = matrix[14];
        this.matrix[3][3] = matrix[15];
    }

    /**
     * 
     * @param x
     * @param y
     * @param z
     * @param w
     */
    public Matrix4f(Vector4f x, Vector4f y, Vector4f z, Vector4f w) {
        this.matrix[0][0] = x.x;
        this.matrix[0][1] = x.y;
        this.matrix[0][2] = x.z;
        this.matrix[0][3] = x.w;
        this.matrix[1][0] = y.x;
        this.matrix[1][1] = y.y;
        this.matrix[1][2] = y.z;
        this.matrix[1][3] = y.w;
        this.matrix[2][0] = z.x;
        this.matrix[2][1] = z.y;
        this.matrix[2][2] = z.z;
        this.matrix[2][3] = z.w;
        this.matrix[3][0] = w.x;
        this.matrix[3][1] = w.y;
        this.matrix[3][2] = w.z;
        this.matrix[3][3] = w.w;
    }

    /**
     * 
     * @param x
     * @param y
     * @param z
     * @param w
     */
    public Matrix4f(float[] x, float[] y, float[] z, float[] w) {
        this.matrix[0][0] = x[0];
        this.matrix[0][1] = x[1];
        this.matrix[0][2] = x[2];
        this.matrix[0][3] = x[3];
        this.matrix[1][0] = y[0];
        this.matrix[1][1] = y[1];
        this.matrix[1][2] = y[2];
        this.matrix[1][3] = y[3];
        this.matrix[2][0] = z[0];
        this.matrix[2][1] = z[1];
        this.matrix[2][2] = z[2];
        this.matrix[2][3] = z[3];
        this.matrix[3][0] = w[0];
        this.matrix[3][1] = w[1];
        this.matrix[3][2] = w[2];
        this.matrix[3][3] = w[3];
    }

    /**
     * 
     * @param xx
     * @param xy
     * @param xz
     * @param xw
     * @param yx
     * @param yy
     * @param yz
     * @param yw
     * @param zx
     * @param zy
     * @param zz
     * @param zw
     * @param wx
     * @param wy
     * @param wz
     * @param ww
     */
    public Matrix4f(float xx, float xy, float xz, float xw, float yx, float yy, float yz, float yw, float zx, float zy, float zz, float zw, float wx, float wy, float wz, float ww) {
        this.matrix[0][0] = xx;
        this.matrix[0][1] = xy;
        this.matrix[0][2] = xz;
        this.matrix[0][3] = xw;
        this.matrix[1][0] = yx;
        this.matrix[1][1] = yy;
        this.matrix[1][2] = yz;
        this.matrix[1][3] = yw;
        this.matrix[2][0] = zx;
        this.matrix[2][1] = zy;
        this.matrix[2][2] = zz;
        this.matrix[2][3] = zw;
        this.matrix[3][0] = wx;
        this.matrix[3][1] = wy;
        this.matrix[3][2] = wz;
        this.matrix[3][3] = ww;
    }

    /**
     * 
     * @return
     */
    public Matrix4f negateMatrix4f() {
        return new Matrix4f(-this.matrix[0][0],
                -this.matrix[0][1],
                -this.matrix[0][2],
                -this.matrix[0][3],
                -this.matrix[1][0],
                -this.matrix[1][1],
                -this.matrix[1][2],
                -this.matrix[1][3],
                -this.matrix[2][0],
                -this.matrix[2][1],
                -this.matrix[2][2],
                -this.matrix[2][3],
                -this.matrix[3][0],
                -this.matrix[3][1],
                -this.matrix[3][2],
                -this.matrix[3][3]);
    }

    /**
     * 
     * @param matrix4f
     * @return
     */
    public Matrix4f addMatrix4f(Matrix4f matrix4f) {
        return new Matrix4f(this.matrix[0][0] + matrix4f.matrix[0][0],
                this.matrix[0][1] + matrix4f.matrix[0][1],
                this.matrix[0][2] + matrix4f.matrix[0][2],
                this.matrix[0][3] + matrix4f.matrix[0][3],
                this.matrix[1][0] + matrix4f.matrix[1][0],
                this.matrix[1][1] + matrix4f.matrix[1][1],
                this.matrix[1][2] + matrix4f.matrix[1][2],
                this.matrix[1][3] + matrix4f.matrix[1][3],
                this.matrix[2][0] + matrix4f.matrix[2][0],
                this.matrix[2][1] + matrix4f.matrix[2][1],
                this.matrix[2][2] + matrix4f.matrix[2][2],
                this.matrix[2][3] + matrix4f.matrix[2][3],
                this.matrix[3][0] + matrix4f.matrix[3][0],
                this.matrix[3][1] + matrix4f.matrix[3][1],
                this.matrix[3][2] + matrix4f.matrix[3][2],
                this.matrix[3][3] + matrix4f.matrix[3][3]);

    }

    /**
     * 
     * @param matrix4f
     */
    public void addMatrix4fToThis(Matrix4f matrix4f) {
        this.matrix[0][0] += matrix4f.matrix[0][0];
        this.matrix[0][1] += matrix4f.matrix[0][1];
        this.matrix[0][2] += matrix4f.matrix[0][2];
        this.matrix[0][3] += matrix4f.matrix[0][3];
        this.matrix[1][0] += matrix4f.matrix[1][0];
        this.matrix[1][1] += matrix4f.matrix[1][1];
        this.matrix[1][2] += matrix4f.matrix[1][2];
        this.matrix[1][3] += matrix4f.matrix[1][3];
        this.matrix[2][0] += matrix4f.matrix[2][0];
        this.matrix[2][1] += matrix4f.matrix[2][1];
        this.matrix[2][2] += matrix4f.matrix[2][2];
        this.matrix[2][3] += matrix4f.matrix[2][3];
        this.matrix[3][0] += matrix4f.matrix[3][0];
        this.matrix[3][1] += matrix4f.matrix[3][1];
        this.matrix[3][2] += matrix4f.matrix[3][2];
        this.matrix[3][3] += matrix4f.matrix[3][3];
    }

    /**
     * 
     * @param matrix4f
     * @return
     */
    public Matrix4f subtractMatrix4f(Matrix4f matrix4f) {

        return new Matrix4f(this.matrix[0][0] - matrix4f.matrix[0][0],
                this.matrix[0][1] - matrix4f.matrix[0][1],
                this.matrix[0][2] - matrix4f.matrix[0][2],
                this.matrix[0][3] - matrix4f.matrix[0][3],
                this.matrix[1][0] - matrix4f.matrix[1][0],
                this.matrix[1][1] - matrix4f.matrix[1][1],
                this.matrix[1][2] - matrix4f.matrix[1][2],
                this.matrix[1][3] - matrix4f.matrix[1][3],
                this.matrix[2][0] - matrix4f.matrix[2][0],
                this.matrix[2][1] - matrix4f.matrix[2][1],
                this.matrix[2][2] - matrix4f.matrix[2][2],
                this.matrix[2][3] - matrix4f.matrix[2][3],
                this.matrix[3][0] - matrix4f.matrix[3][0],
                this.matrix[3][1] - matrix4f.matrix[3][1],
                this.matrix[3][2] - matrix4f.matrix[3][2],
                this.matrix[3][3] - matrix4f.matrix[3][3]);

    }

    /**
     * 
     * @param matrix4f
     */
    public void subtractMatrix4fToThis(Matrix4f matrix4f) {
        this.matrix[0][0] -= matrix4f.matrix[0][0];
        this.matrix[0][1] -= matrix4f.matrix[0][1];
        this.matrix[0][2] -= matrix4f.matrix[0][2];
        this.matrix[0][3] -= matrix4f.matrix[0][3];
        this.matrix[1][0] -= matrix4f.matrix[1][0];
        this.matrix[1][1] -= matrix4f.matrix[1][1];
        this.matrix[1][2] -= matrix4f.matrix[1][2];
        this.matrix[1][3] -= matrix4f.matrix[1][3];
        this.matrix[2][0] -= matrix4f.matrix[2][0];
        this.matrix[2][1] -= matrix4f.matrix[2][1];
        this.matrix[2][2] -= matrix4f.matrix[2][2];
        this.matrix[2][3] -= matrix4f.matrix[2][3];
        this.matrix[3][0] -= matrix4f.matrix[3][0];
        this.matrix[3][1] -= matrix4f.matrix[3][1];
        this.matrix[3][2] -= matrix4f.matrix[3][2];
        this.matrix[3][3] -= matrix4f.matrix[3][3];
    }

    /**
     * 
     * @param scale
     * @return
     */
    public Matrix4f scaleMatrix4f(float scale) {

        return new Matrix4f(this.matrix[0][0] * scale,
                this.matrix[0][1] * scale,
                this.matrix[0][2] * scale,
                this.matrix[0][3] * scale,
                this.matrix[1][0] * scale,
                this.matrix[1][1] * scale,
                this.matrix[1][2] * scale,
                this.matrix[1][3] * scale,
                this.matrix[2][0] * scale,
                this.matrix[2][1] * scale,
                this.matrix[2][2] * scale,
                this.matrix[2][3] * scale,
                this.matrix[3][0] * scale,
                this.matrix[3][1] * scale,
                this.matrix[3][2] * scale,
                this.matrix[3][3] * scale);

    }

    /**
     * 
     * @param scale
     */
    public void scaleMatrix4fToThis(float scale) {
        this.matrix[0][0] *= scale;
        this.matrix[0][1] *= scale;
        this.matrix[0][2] *= scale;
        this.matrix[0][3] *= scale;
        this.matrix[1][0] *= scale;
        this.matrix[1][1] *= scale;
        this.matrix[1][2] *= scale;
        this.matrix[1][3] *= scale;
        this.matrix[2][0] *= scale;
        this.matrix[2][1] *= scale;
        this.matrix[2][2] *= scale;
        this.matrix[2][3] *= scale;
        this.matrix[3][0] *= scale;
        this.matrix[3][1] *= scale;
        this.matrix[3][2] *= scale;
        this.matrix[3][3] *= scale;
    }

    /**
     * 
     * @param matrix4f
     * @return
     */
    public Matrix4f multiplyMatrix4f(Matrix4f matrix4f) {

        return new Matrix4f(this.matrix[0][0] * matrix4f.matrix[0][0],
                this.matrix[0][1] * matrix4f.matrix[0][1],
                this.matrix[0][2] * matrix4f.matrix[0][2],
                this.matrix[0][3] * matrix4f.matrix[0][3],
                this.matrix[1][0] * matrix4f.matrix[1][0],
                this.matrix[1][1] * matrix4f.matrix[1][1],
                this.matrix[1][2] * matrix4f.matrix[1][2],
                this.matrix[1][3] * matrix4f.matrix[1][3],
                this.matrix[2][0] * matrix4f.matrix[2][0],
                this.matrix[2][1] * matrix4f.matrix[2][1],
                this.matrix[2][2] * matrix4f.matrix[2][2],
                this.matrix[2][3] * matrix4f.matrix[2][3],
                this.matrix[3][0] * matrix4f.matrix[3][0],
                this.matrix[3][1] * matrix4f.matrix[3][1],
                this.matrix[3][2] * matrix4f.matrix[3][2],
                this.matrix[3][3] * matrix4f.matrix[3][3]);

    }

    /**
     * 
     * @param matrix4f
     */
    public void multiplyMatrix4fToThis(Matrix4f matrix4f) {

        this.matrix[0][0] *= matrix4f.matrix[0][0];
        this.matrix[0][1] *= matrix4f.matrix[0][1];
        this.matrix[0][2] *= matrix4f.matrix[0][2];
        this.matrix[0][3] *= matrix4f.matrix[0][3];
        this.matrix[1][0] *= matrix4f.matrix[1][0];
        this.matrix[1][1] *= matrix4f.matrix[1][1];
        this.matrix[1][2] *= matrix4f.matrix[1][2];
        this.matrix[1][3] *= matrix4f.matrix[1][3];
        this.matrix[2][0] *= matrix4f.matrix[2][0];
        this.matrix[2][1] *= matrix4f.matrix[2][1];
        this.matrix[2][2] *= matrix4f.matrix[2][2];
        this.matrix[2][3] *= matrix4f.matrix[2][3];
        this.matrix[3][0] *= matrix4f.matrix[3][0];
        this.matrix[3][1] *= matrix4f.matrix[3][1];
        this.matrix[3][2] *= matrix4f.matrix[3][2];
        this.matrix[3][3] *= matrix4f.matrix[3][3];

    }

    /**
     * 
     * @param row
     * @param vector4f
     */
    public void setRow(int row, Vector4f vector4f) {
        matrix[row][0] = vector4f.x;
        matrix[row][1] = vector4f.y;
        matrix[row][2] = vector4f.z;
        matrix[row][3] = vector4f.w;

    }

    /**
     * 
     * @param column
     * @param vector4f
     */
    public void setColumn(int column, Vector4f vector4f) {
        matrix[0][column] = vector4f.x;
        matrix[1][column] = vector4f.y;
        matrix[2][column] = vector4f.z;
        matrix[3][column] = vector4f.w;
    }

    /**
     * 
     * @param row
     * @return
     */
    public Vector4f getRow(int row) {
        return new Vector4f(matrix[row][0], matrix[row][1], matrix[row][2], matrix[row][3]);
    }

    /**
     * 
     * @param column
     * @return
     */
    public Vector4f getColumn(int column) {
        return new Vector4f(matrix[0][column], matrix[1][column], matrix[2][column], matrix[3][column]);
    }

    /**
     * 
     */
    public void setIdentity() {
        this.matrix[0][0] = 1f;
        this.matrix[0][1] = 0f;
        this.matrix[0][2] = 0f;
        this.matrix[0][3] = 0f;
        this.matrix[1][0] = 0f;
        this.matrix[1][1] = 1f;
        this.matrix[1][2] = 0f;
        this.matrix[1][3] = 0f;
        this.matrix[2][0] = 0f;
        this.matrix[2][1] = 0f;
        this.matrix[2][2] = 1f;
        this.matrix[2][3] = 0f;
        this.matrix[3][0] = 0f;
        this.matrix[3][1] = 0f;
        this.matrix[3][2] = 0f;
        this.matrix[3][3] = 1f;
    }

    /**
     * 
     */
    public void setZero() {
        this.matrix[0][0] = 0f;
        this.matrix[0][1] = 0f;
        this.matrix[0][2] = 0f;
        this.matrix[0][3] = 0f;
        this.matrix[1][0] = 0f;
        this.matrix[1][1] = 0f;
        this.matrix[1][2] = 0f;
        this.matrix[1][3] = 0f;
        this.matrix[2][0] = 0f;
        this.matrix[2][1] = 0f;
        this.matrix[2][2] = 0f;
        this.matrix[2][3] = 0f;
        this.matrix[3][0] = 0f;
        this.matrix[3][1] = 0f;
        this.matrix[3][2] = 0f;
        this.matrix[3][3] = 0f;
    }

    /**
     * 
     */
    public void transpose() {
        float i = matrix[0][1];
        i = matrix[0][1];
        matrix[0][1] = matrix[1][0];
        matrix[1][0] = i;
        i = matrix[0][2];
        matrix[0][2] = matrix[2][0];
        matrix[2][0] = i;
        i = matrix[1][2];
        matrix[1][2] = matrix[2][1];
        matrix[2][1] = i;
        i = matrix[0][3];
        matrix[0][3] = matrix[3][0];
        matrix[3][0] = i;
        i = matrix[1][3];
        matrix[1][3] = matrix[3][1];
        matrix[3][1] = i;
        i = matrix[2][3];
        matrix[2][3] = matrix[3][2];
        matrix[3][2] = i;

    }

    /**
     * 
     * @param Matrix4f
     */
    public void getTranspose(Matrix4f Matrix4f) {

        Matrix4f.matrix[0][0] = matrix[0][0];
        Matrix4f.matrix[0][1] = matrix[0][1];
        Matrix4f.matrix[0][2] = matrix[0][2];
        Matrix4f.matrix[0][3] = matrix[0][3];
        Matrix4f.matrix[1][0] = matrix[1][0];
        Matrix4f.matrix[1][1] = matrix[1][1];
        Matrix4f.matrix[1][2] = matrix[1][2];
        Matrix4f.matrix[1][3] = matrix[1][3];
        Matrix4f.matrix[2][0] = matrix[2][0];
        Matrix4f.matrix[2][1] = matrix[2][1];
        Matrix4f.matrix[2][2] = matrix[2][2];
        Matrix4f.matrix[2][3] = matrix[2][3];
        Matrix4f.matrix[3][0] = matrix[3][0];
        Matrix4f.matrix[3][1] = matrix[3][1];
        Matrix4f.matrix[3][2] = matrix[3][2];
        Matrix4f.matrix[3][3] = matrix[3][3];
    }

    /**
     * 
     * @return
     */
    public float determinate() {
        return matrix[0][0] * (matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1]) + matrix[0][1] * (matrix[1][2] * matrix[2][0] - matrix[1][0] * matrix[2][2]) + matrix[0][2] * (matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0]);
    }

    /**
     * 
     * @param vector4f
     */
    public void scale(Vector4f vector4f) {
        this.matrix[0][0] = vector4f.x;
        this.matrix[0][1] = 0f;
        this.matrix[0][2] = 0f;
        this.matrix[0][3] = 0f;
        this.matrix[1][0] = 0f;
        this.matrix[1][1] = vector4f.y;
        this.matrix[1][2] = 0f;
        this.matrix[1][3] = 0f;
        this.matrix[2][0] = 0f;
        this.matrix[2][1] = 0f;
        this.matrix[2][2] = vector4f.z;
        this.matrix[2][3] = 0f;
        this.matrix[3][0] = 0f;
        this.matrix[3][1] = 0f;
        this.matrix[3][2] = 0f;
        this.matrix[3][3] = vector4f.w;


    }

    /**
     * 
     * @param x
     * @param y
     * @param z
     * @param w
     */
    public void scale(float x, float y, float z, float w) {
        this.matrix[0][0] = x;
        this.matrix[0][1] = 0f;
        this.matrix[0][2] = 0f;
        this.matrix[0][3] = 0f;
        this.matrix[1][0] = 0f;
        this.matrix[1][1] = y;
        this.matrix[1][2] = 0f;
        this.matrix[1][3] = 0f;
        this.matrix[2][0] = 0f;
        this.matrix[2][1] = 0f;
        this.matrix[2][2] = z;
        this.matrix[2][3] = 0f;
        this.matrix[3][0] = 0f;
        this.matrix[3][1] = 0f;
        this.matrix[3][2] = 0f;
        this.matrix[3][3] = w;
    }

    /**
     * 
     * @param scale
     */
    public void scale(float scale) {

        scale(scale, scale, scale, scale);

    }

    /**
     * 
     * @param angle
     */
    public void rotateX(float angle) {

        float cosAngle = (float) Math.cos(angle);
        float sinAngle = (float) Math.sin(angle);
        this.matrix[0][0] = 1f;
        this.matrix[0][1] = 0f;
        this.matrix[0][2] = 0f;
        this.matrix[0][3] = 0f;
        this.matrix[1][0] = 0f;
        this.matrix[1][1] = cosAngle;
        this.matrix[1][2] = sinAngle;
        this.matrix[1][3] = 0f;
        this.matrix[2][0] = 0f;
        this.matrix[2][1] = -sinAngle;
        this.matrix[2][2] = cosAngle;
        this.matrix[2][3] = 0f;
        this.matrix[3][0] = 0f;
        this.matrix[3][1] = 0f;
        this.matrix[3][2] = 0f;
        this.matrix[3][3] = 1f;

    }

    /**
     * 
     * @param angle
     */
    public void rotateY(float angle) {
        float cosAngle = (float) Math.cos(angle);
        float sinAngle = (float) Math.sin(angle);
        this.matrix[0][0] = cosAngle;
        this.matrix[0][1] = 0f;
        this.matrix[0][2] = -sinAngle;
        this.matrix[0][3] = 0f;
        this.matrix[1][0] = 0f;
        this.matrix[1][1] = 1f;
        this.matrix[1][2] = 0f;
        this.matrix[1][3] = 0f;
        this.matrix[2][0] = sinAngle;
        this.matrix[2][1] = 0f;
        this.matrix[2][2] = cosAngle;
        this.matrix[2][3] = 0f;
        this.matrix[3][0] = 0f;
        this.matrix[3][1] = 0f;
        this.matrix[3][2] = 0f;
        this.matrix[3][3] = 1f;
    }

    /**
     * 
     * @param angle
     */
    public void rotateZ(float angle) {
        float cosAngle = (float) Math.cos(angle);
        float sinAngle = (float) Math.sin(angle);
        this.matrix[0][0] = cosAngle;
        this.matrix[0][1] = sinAngle;
        this.matrix[0][2] = 0f;
        this.matrix[0][3] = 0f;
        this.matrix[1][0] = -sinAngle;
        this.matrix[1][1] = cosAngle;
        this.matrix[1][2] = 0f;
        this.matrix[1][3] = 0f;
        this.matrix[2][0] = 0f;
        this.matrix[2][1] = 0f;
        this.matrix[2][2] = 1f;
        this.matrix[2][3] = 0f;
        this.matrix[3][0] = 0f;
        this.matrix[3][1] = 0f;
        this.matrix[3][2] = 0f;
        this.matrix[3][3] = 1f;
    }

    /**
     * 
     * @param angle
     */
    public void rotareXYZ(Vector3f angle) {
        

        this.rotateX(angle.x);
        this.rotateY(angle.y);
        this.rotateZ(angle.z);


    }

    /**
     * 
     * @param vector3f
     * @return
     */
    public Vector3f transform(Vector3f vector3f) {
        
        return new Vector3f(
                
        vector3f.x * matrix[0][0] + vector3f.y * matrix[1][0] + vector3f.z * matrix[2][0] + matrix[3][0],
        vector3f.x * matrix[0][1] + vector3f.y * matrix[1][1] + vector3f.z * matrix[2][1] + matrix[3][1],
        vector3f.x * matrix[0][2] + vector3f.y * matrix[1][2] + vector3f.z * matrix[2][2] + matrix[3][2]
                );
    }

    @Override
    public String toString() {
        return "Matrix3f{" + "matrix=" + matrix[0][0] + " "
                + matrix[0][1] + " "
                + matrix[0][2] + " "
                + matrix[1][0] + " "
                + matrix[1][1] + " "
                + matrix[1][2] + " "
                + matrix[2][0] + " "
                + matrix[2][1] + " "
                + matrix[2][2];
    }
}
